TOOL.Tab 		= "Fallen Menu"
TOOL.Category	= "Construction"
TOOL.Name = "#Constraint Remover Tool"
TOOL.Command = nil
TOOL.ConfigName = ""

// Tables

local ConstraintBreaker = {}

ConstraintBreaker.Constraints = {
	{"phys_constraint", "Welds"},
	{"phys_lengthconstraint", "Ropes"},
	{"phys_spring", "Elastic"},
	{"phys_keepupright", "Keep Uprights"},
	{"phys_slideconstraint", "Sliders"},
	{"phys_hinge", "Axis"},
	{"logic_collision_pair", "Nocollides"},
	{"phys_torque", "Motors"},
	{"phys_pulleyconstraint", "Pulleys"},
	{"phys_ballsocket", "Ballsockets"},
	{"gmod_winch_controller", "Winches/Hydraulics/Muscles"}
}

// ConVars

TOOL.ClientConVar["Key"] = "5"
TOOL.ClientConVar["Delay"] = "0"
TOOL.ClientConVar["Delete"] = "0"
TOOL.ClientConVar["Connected"] = "0"

for K, V in pairs(ConstraintBreaker.Constraints) do
	local Type = V[1]
	
	TOOL.ClientConVar[Type] = "1"
end

// Friendly

function ConstraintBreaker.Friendly(Entity, ID)
	if (Entity.ConstraintBreaker[ID] and Entity.ConstraintBreaker[ID] == 1) then
		return true
	end
	
	return false
end

// Client

if (CLIENT) then
	language.Add("Tool_constraintbreaker_name", "Constraint Breaker Tool")
	language.Add("Tool_constraintbreaker_desc", "Makes all constraints connected to an entity break when a key is pressed")
	language.Add("Tool_constraintbreaker_0", "Left click on an entity to add a Constraint Breaker")
	
	language.Add("Undone_constraintbreaker", "Undone Constraint Breaker")
end

// Message

function TOOL:Message(Text)
	if SERVER then
		self:GetOwner():SendLua("GAMEMODE:AddNotify('"..Text.."', NOTIFY_GENERIC, 10)")
		self:GetOwner():SendLua("surface.PlaySound('ambient/water/drip"..math.random(1, 4)..".wav')")
	end
end

// Get entities

function ConstraintBreaker.GetConstraints(Entity)
	local Constraints = {}
	
	for K, V in pairs(Entity.Constraints) do
		local Index = V
		
		if (!Constraints[Index]) then
			Constraints[Index] = V
		end
	end
	
	return Constraints
end

// Get entities

local function ExtractEntities(Entity, Entities, Constraints)
	Constraints = Constraints or {}
	
	Entities = Entities or {}
	
	if (!Entity:IsValid()) then return Entities, Constraints end

	Entities[Entity:EntIndex()] = Entity
	
	if (!constraint.HasConstraints(Entity)) then return Entities, Constraints end
	
	local Cons = constraint.GetTable(Entity)
	
	for K, V in pairs(Cons) do
		local Index = V.Constraint
		
		if (!Constraints[Index]) then
			Constraints[Index] = V.Constraint
			
			for B, J in pairs(V.Entity) do
				local E, C = ExtractEntities(J.Entity, Entities, Constraints)
				
				table.Merge(Entities, E)
				table.Merge(Constraints, C)
			end
		end
	end
	
	return Entities, Constraints
end

// Left click

function TOOL:LeftClick(Trace)
	if Trace.Entity then
		if !Trace.Entity:IsValid() or Trace.Entity:IsPlayer() or Trace.HitWorld or Trace.Entity:IsNPC() then
			return false
		end
	end
	
	if(CLIENT) then
		return true
	end

	if(!SERVER) then return false end
	
	// Locals
	
	local User = self:GetOwner()
	
	local Entity = Trace.Entity

	// Values
	
	local Key = self:GetClientNumber("Key")
	local Delay = self:GetClientNumber("Delay")
	local Delete = self:GetClientNumber("Delete")
	local Connected = self:GetClientNumber("Connected")
	
	// Nice and clean
	
	Delay = math.floor(Delay)
	
	// Check
	
	if (Delay < 0) then
		self:GetOwner():ConCommand("constraintbreaker_Delay 0\n")
		
		Delay = 0
	end
	
	// ConstraintBreaker table for entity
	
	Entity.ConstraintBreaker = {}
	
	// Values
	
	Entity.ConstraintBreaker.Connected = Connected
	Entity.ConstraintBreaker.Delay = Delay
	Entity.ConstraintBreaker.Delete = Delete
	Entity.ConstraintBreaker.ID = numpad.OnUp(User, Key, "ConstraintBreaker", Entity, false)
	
	// Constraints
	
	for K, V in pairs(ConstraintBreaker.Constraints) do
		local Type = V[1]
		
		Entity.ConstraintBreaker[Type] = self:GetClientNumber(Type)
	end
	
	// Undo
	
	local function Function(Undo, Entity)
		if (Entity:IsValid()) then
			numpad.Remove(Entity.ConstraintBreaker.ID)
			
			Entity.ConstraintBreaker = nil
		end
	end
	
	undo.Create("constraintbreaker")
		undo.SetPlayer(User)
		undo.AddFunction(Function, Entity)
	undo.Finish()
	
	// Message
	
	self:Message("Constraint Breaker added to entity, press "..Key.." to activate!")
	
	return true
end

// Pressed key

if SERVER then
	function TOOL.Pressed(Player, Entity, Key, IDX)
		if !Entity:IsValid() then return end
		
		if not Key then
			local ID = Entity.ConstraintBreaker.ID
			
			// Remove the numpad ID
			
			if (Entity.ConstraintBreaker.Delete == 1) then
				numpad.Remove(ID)
			end
			
			// Timed function
			
			local function Function(Entity)
				if !Entity:IsValid() then return end
				
				local Constraints = {}
				
				local Entities = {}
				
				if (Entity.ConstraintBreaker.Connected == 1) then
					Entities, Constraints = ExtractEntities(Entity)
				else
					Constraints = ConstraintBreaker.GetConstraints(Entity)
				end
				
				for K, V in pairs(Constraints) do
					if (V) then
						local Valid = V:IsValid()
						
						if (Valid) then
							local Class = V:GetClass()
							
							if (ConstraintBreaker.Friendly(Entity, Class)) then
								V:Remove()
							end
						end
					end
				end
				
				local Random = math.random(1, 3)
				
				local Emit = Sound("physics/metal/metal_chainlink_impact_hard"..Random..".wav")
				
				Entity:EmitSound(Emit)
			end
			
			timer.Create("ConstraintBreaker: "..Entity:EntIndex(), Entity.ConstraintBreaker.Delay, 1, Function, Entity)
		end
	end
	
	numpad.Register("ConstraintBreaker", TOOL.Pressed)
end

// Build CPanel

function TOOL.BuildCPanel(Panel)
	Panel:AddControl("Header", {Text = "#Tool_constraintbreaker_name", Description	= "#Tool_constraintbreaker_desc"})
	
	Panel:AddControl("Numpad", {Label = "Button", ButtonSize = "22", Command = "constraintbreaker_Key"})
	
	Panel:AddControl("Slider", {Label = "Delay untill constraints break", Type = "Integer", Min = 0, Max = 10, Command = "constraintbreaker_Delay"})
	
	Panel:AddControl("CheckBox", {Label = "Delete Constraint Breaker after use", Command = "constraintbreaker_Delete"})

	Panel:AddControl("CheckBox", {Label = "Break constraints on entities connected to the entity", Command = "constraintbreaker_Connected"})
	
	for K, V in pairs(ConstraintBreaker.Constraints) do
		local Type = V[1]
		local ID   = V[2]
		
		Panel:AddControl("CheckBox", {Label = "Break "..ID, Command = "constraintbreaker_"..Type})
	end
end